Python Programlamaya Giriş 21 - Dinamik kod üretme

Python Programlamaya Giriş
Author

Kaan Öztürk

Published

June 8, 2018

Python dinamik tabir edilen dillerden biridir. Programda kullandığıız değişkenlerin tiplerini baştan bildirmeniz gerekmez. Program çalıştıkça işlenen komutlar, yeni nesneleri anında üretir. Bu dinamiklik sayesinde, dize olarak verilmiş Python komutlarını da işleyebilir, hatta program yazan programlar yazabiliriz.

Bu işlemi yapmak için iki Python fonksiyonu vardır: eval() ve exec()

Dizinin bütün yazılarına erişmek için Python Programlamaya Giriş kategorimize bakabilirsiniz. Bu dizideki yazılar ayrıca Jupyter defterleri halinde GitHub depomuzda da mevcut.

eval

Bu fonksiyon, Python komutları içeren bir dizeyi yorumlayıcıya gönderir ve sonucu geri verir.

eval("2**3 + 4*5")
28

Çalıştırma anında isim alanında bulunan değişkenler de kod dizesi içinde kullanılabilir.

x = 5
eval("2*x+4")
14

Komut dizesi içindeki değişkeni başka bir değerle kullanmak isterseniz, değişkenleri bir sözlük ile verebilirsiniz.

eval("2*x+4",{"x":10})
24

Örnek: Basit hesap makinesi.

Kullanıcıdan tek tek matematiksel ifadeler alıp sonucu yazan bir programcık yazalım. Kullanıcı “dur” yazdığında program sona ersin.

işlemler = """1 + 2 + 3 + 4 + 5
2**2 + 3 * 5
1/10 + 1/5
"""
for işlem in işlemler.splitlines():
    print(işlem, " = ", eval(işlem))
1 + 2 + 3 + 4 + 5  =  15
2**2 + 3 * 5  =  19
1/10 + 1/5  =  0.30000000000000004

Örnek: Yardım belgeleri özetleri

Nesnelerin yardım belgelerini daha önce kullanmıştık. Bir metodla ilgili bilgi almak için help() komutunu kullandığımızda o metodun (bir fonksiyon nesnesidir) __doc__ isimli özelliği ekrana basılır. Buna doğrudan da erişebiliriz.

list.append.__doc__
'L.append(object) -> None -- append object to end'

Diyelim bir nesne sınıfı altında tanımlanmış bütün metodların kısa tarifini (belge dizesini) ekrana dökmek istiyoruz. Ama bir sınıf altında, daha özel amaçlı, doğrudan kullanılmayan metodlar da bulunur. Bunların başlarında ve sonlarında bir çift altçizgi (“dunder”) bulunur.

Çift altçizgili metodları hariç tutarak, metodların kısa açıklamalarını listelemek için eval’i bir döngü içinde kullanabiliriz.

for metod in dir(list):
    if "__" not in metod:
        print(eval("list."+metod+".__doc__"))
L.append(object) -> None -- append object to end
L.clear() -> None -- remove all items from L
L.copy() -> list -- a shallow copy of L
L.count(value) -> integer -- return number of occurrences of value
L.extend(iterable) -> None -- extend list by appending elements from the iterable
L.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
L.insert(index, object) -- insert object before index
L.pop([index]) -> item -- remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
L.remove(value) -> None -- remove first occurrence of value.
Raises ValueError if the value is not present.
L.reverse() -- reverse *IN PLACE*
L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*

exec

Değer döndüren ifadeleri eval() ile işletebiliriz, ama bir ifade (expression) olmayan, yani değer döndürmeyen komutları (söz gelişi, modül yükleme, fonksiyon tanımları, döngüler, atamalar vb.) çalıştırmak için exec() fonksiyonuna ihtiyacımız var.

Örnek olarak, bir değişken ataması yapalım:

exec("""
import math
x=math.pi""")
x
3.141592653589793

Bir formülde değişkene 0-9 arası değerler vererek bir tablo oluşturan bir kod yazalım.

değişken = "x"
formül = "1/(1+x) + x"
kod = """
for {0} in range(10):
    print({0}, {1})""".format(değişken, formül)
print(kod)

for x in range(10):
    print(x, 1/(1+x) + x)
exec(kod)
0 1.0
1 1.5
2 2.3333333333333335
3 3.25
4 4.2
5 5.166666666666667
6 6.142857142857143
7 7.125
8 8.11111111111111
9 9.1

Farkedeceğiniz gibi, değişken’in değeri "x" olduğu için formülde de x karakterini kullanmamız gerekiyor.

Global ve yerel değişkenler

eval/exec fonksiyonları, işletilecek kodu barındıran dizenin yanı sıra iki parametre daha alırlar: globals ve locals. Bu parametreler özellikle belirtilmezse, eval/exec kodunda yorumlayıcının o andaki durumunda tanımlanmış olan bütün isimler kullanılabilir.

Yerel değişkenler bir fonksiyon içinden tanımlı olan, o fonksiyonun dışında tanınmayan isimlerdir. Global değişkenler ise bütün fonksiyonların erişebileceği değişkenlerdir. Yerel isimlere locals(), global isimlere ise globals() komutlarıyla ulaşılabilir. Bu komutlar değişken isimleriyle değerlerini eşleştiren birer sözlük döndürür.

def f(x):
    a = 10
    print(locals())

f(3)
{'a': 10, 'x': 3}

eval/exec ile bir kod parçası çalıştırırken bu global ve yerel değişkenleri sınırlandırabiliriz. Bu fonksiyonların genel kullanımı şöyledir:

eval(source, globals=None, locals=None)
exec(source, globals=None, locals=None)

Burada globals ve locals parametreleri için global ve yerel değişkenleri tutan birer sözlük koyabiliriz.

exec("print(locals())", None, {"abc": 17, "xyz": "Mehmet"})
{'abc': 17, 'xyz': 'Mehmet'}
exec("print(globals())", None, {"abc": 17, "xyz": "Mehmet"})
{'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', 'eval("2**3 + 4*5")', 'x = 5\neval("2*x+4")', 'eval("2*x+4",{"x":10})', 'while(True):\n    işlem = input("Bir işlem yazın: ")\n    if işlem.strip().lower()=="dur": break\n    print(eval(işlem))', 'list.append.__doc__', 'dir(list)', 'for metod in dir(list):\n    if "__" not in metod:\n        print(eval("list."+metod+".__doc__"))', 'exec("x=3.1415")\nx', 'değişken = "x" # Formülde kullanılacak değişken.\nformül = input("Bir matematiksel formül yazın: ")\nkod = """\nfor {0} in range(10):\n    print({0}, {1})""".format(değişken, formül)\nprint(kod)', 'exec(kod)', 'globals()', 'def f(x):\n    a = 10\n    print(locals())\n\nf(3)', 'exec("print(locals())", None, {"abc": 17, "xyz": "Mehmet"})', 'exec("print(globals())", None, {"abc": 17, "xyz": "Mehmet"})'], '_oh': {1: 28, 2: 14, 3: 24, 5: 'L.append(object) -> None -- append object to end', 6: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'], 8: 3.1415, 11: {...}}, '_dh': ['/home/kaan/git/VeriDefteri/Python_Programlama'], 'In': ['', 'eval("2**3 + 4*5")', 'x = 5\neval("2*x+4")', 'eval("2*x+4",{"x":10})', 'while(True):\n    işlem = input("Bir işlem yazın: ")\n    if işlem.strip().lower()=="dur": break\n    print(eval(işlem))', 'list.append.__doc__', 'dir(list)', 'for metod in dir(list):\n    if "__" not in metod:\n        print(eval("list."+metod+".__doc__"))', 'exec("x=3.1415")\nx', 'değişken = "x" # Formülde kullanılacak değişken.\nformül = input("Bir matematiksel formül yazın: ")\nkod = """\nfor {0} in range(10):\n    print({0}, {1})""".format(değişken, formül)\nprint(kod)', 'exec(kod)', 'globals()', 'def f(x):\n    a = 10\n    print(locals())\n\nf(3)', 'exec("print(locals())", None, {"abc": 17, "xyz": "Mehmet"})', 'exec("print(globals())", None, {"abc": 17, "xyz": "Mehmet"})'], 'Out': {1: 28, 2: 14, 3: 24, 5: 'L.append(object) -> None -- append object to end', 6: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'], 8: 3.1415, 11: {...}}, 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f9d822f7b70>>, 'exit': <IPython.core.autocall.ZMQExitAutocall object at 0x7f9d80260630>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x7f9d80260630>, '_': {...}, '__': 3.1415, '___': ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'], '_i': 'exec("print(locals())", None, {"abc": 17, "xyz": "Mehmet"})', '_ii': 'def f(x):\n    a = 10\n    print(locals())\n\nf(3)', '_iii': 'globals()', '_i1': 'eval("2**3 + 4*5")', '_1': 28, '_i2': 'x = 5\neval("2*x+4")', 'x': 9, '_2': 14, '_i3': 'eval("2*x+4",{"x":10})', '_3': 24, '_i4': 'while(True):\n    işlem = input("Bir işlem yazın: ")\n    if işlem.strip().lower()=="dur": break\n    print(eval(işlem))', 'işlem': 'dur', '_i5': 'list.append.__doc__', '_5': 'L.append(object) -> None -- append object to end', '_i6': 'dir(list)', '_6': ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'], '_i7': 'for metod in dir(list):\n    if "__" not in metod:\n        print(eval("list."+metod+".__doc__"))', 'metod': 'sort', '_i8': 'exec("x=3.1415")\nx', '_8': 3.1415, '_i9': 'değişken = "x" # Formülde kullanılacak değişken.\nformül = input("Bir matematiksel formül yazın: ")\nkod = """\nfor {0} in range(10):\n    print({0}, {1})""".format(değişken, formül)\nprint(kod)', 'değişken': 'x', 'formül': '2*x**3-10', 'kod': '\nfor x in range(10):\n    print(x, 2*x**3-10)', '_i10': 'exec(kod)', '_i11': 'globals()', '_11': {...}, '_i12': 'def f(x):\n    a = 10\n    print(locals())\n\nf(3)', 'f': <function f at 0x7f9d80165d08>, '_i13': 'exec("print(locals())", None, {"abc": 17, "xyz": "Mehmet"})', '_i14': 'exec("print(globals())", None, {"abc": 17, "xyz": "Mehmet"})'}

globals yerine boş bir sözlük koyarsak, sadece Python’da tanımlanmış (built-in) isimlere erişilebilir sadece

exec("print(globals())", {}, {"abc": 17, "xyz": "Mehmet"})
{'__builtins__': {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>, 'exec': <built-in function exec>, 'format': <built-in function format>, 'getattr': <built-in function getattr>, 'globals': <built-in function globals>, 'hasattr': <built-in function hasattr>, 'hash': <built-in function hash>, 'hex': <built-in function hex>, 'id': <built-in function id>, 'input': <bound method Kernel.raw_input of <ipykernel.ipkernel.IPythonKernel object at 0x7f9d822f7f28>>, 'isinstance': <built-in function isinstance>, 'issubclass': <built-in function issubclass>, 'iter': <built-in function iter>, 'len': <built-in function len>, 'locals': <built-in function locals>, 'max': <built-in function max>, 'min': <built-in function min>, 'next': <built-in function next>, 'oct': <built-in function oct>, 'ord': <built-in function ord>, 'pow': <built-in function pow>, 'print': <built-in function print>, 'repr': <built-in function repr>, 'round': <built-in function round>, 'setattr': <built-in function setattr>, 'sorted': <built-in function sorted>, 'sum': <built-in function sum>, 'vars': <built-in function vars>, 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': <class 'bool'>, 'memoryview': <class 'memoryview'>, 'bytearray': <class 'bytearray'>, 'bytes': <class 'bytes'>, 'classmethod': <class 'classmethod'>, 'complex': <class 'complex'>, 'dict': <class 'dict'>, 'enumerate': <class 'enumerate'>, 'filter': <class 'filter'>, 'float': <class 'float'>, 'frozenset': <class 'frozenset'>, 'property': <class 'property'>, 'int': <class 'int'>, 'list': <class 'list'>, 'map': <class 'map'>, 'object': <class 'object'>, 'range': <class 'range'>, 'reversed': <class 'reversed'>, 'set': <class 'set'>, 'slice': <class 'slice'>, 'staticmethod': <class 'staticmethod'>, 'str': <class 'str'>, 'super': <class 'super'>, 'tuple': <class 'tuple'>, 'type': <class 'type'>, 'zip': <class 'zip'>, '__debug__': True, 'BaseException': <class 'BaseException'>, 'Exception': <class 'Exception'>, 'TypeError': <class 'TypeError'>, 'StopAsyncIteration': <class 'StopAsyncIteration'>, 'StopIteration': <class 'StopIteration'>, 'GeneratorExit': <class 'GeneratorExit'>, 'SystemExit': <class 'SystemExit'>, 'KeyboardInterrupt': <class 'KeyboardInterrupt'>, 'ImportError': <class 'ImportError'>, 'ModuleNotFoundError': <class 'ModuleNotFoundError'>, 'OSError': <class 'OSError'>, 'EnvironmentError': <class 'OSError'>, 'IOError': <class 'OSError'>, 'EOFError': <class 'EOFError'>, 'RuntimeError': <class 'RuntimeError'>, 'RecursionError': <class 'RecursionError'>, 'NotImplementedError': <class 'NotImplementedError'>, 'NameError': <class 'NameError'>, 'UnboundLocalError': <class 'UnboundLocalError'>, 'AttributeError': <class 'AttributeError'>, 'SyntaxError': <class 'SyntaxError'>, 'IndentationError': <class 'IndentationError'>, 'TabError': <class 'TabError'>, 'LookupError': <class 'LookupError'>, 'IndexError': <class 'IndexError'>, 'KeyError': <class 'KeyError'>, 'ValueError': <class 'ValueError'>, 'UnicodeError': <class 'UnicodeError'>, 'UnicodeEncodeError': <class 'UnicodeEncodeError'>, 'UnicodeDecodeError': <class 'UnicodeDecodeError'>, 'UnicodeTranslateError': <class 'UnicodeTranslateError'>, 'AssertionError': <class 'AssertionError'>, 'ArithmeticError': <class 'ArithmeticError'>, 'FloatingPointError': <class 'FloatingPointError'>, 'OverflowError': <class 'OverflowError'>, 'ZeroDivisionError': <class 'ZeroDivisionError'>, 'SystemError': <class 'SystemError'>, 'ReferenceError': <class 'ReferenceError'>, 'BufferError': <class 'BufferError'>, 'MemoryError': <class 'MemoryError'>, 'Warning': <class 'Warning'>, 'UserWarning': <class 'UserWarning'>, 'DeprecationWarning': <class 'DeprecationWarning'>, 'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>, 'SyntaxWarning': <class 'SyntaxWarning'>, 'RuntimeWarning': <class 'RuntimeWarning'>, 'FutureWarning': <class 'FutureWarning'>, 'ImportWarning': <class 'ImportWarning'>, 'UnicodeWarning': <class 'UnicodeWarning'>, 'BytesWarning': <class 'BytesWarning'>, 'ResourceWarning': <class 'ResourceWarning'>, 'ConnectionError': <class 'ConnectionError'>, 'BlockingIOError': <class 'BlockingIOError'>, 'BrokenPipeError': <class 'BrokenPipeError'>, 'ChildProcessError': <class 'ChildProcessError'>, 'ConnectionAbortedError': <class 'ConnectionAbortedError'>, 'ConnectionRefusedError': <class 'ConnectionRefusedError'>, 'ConnectionResetError': <class 'ConnectionResetError'>, 'FileExistsError': <class 'FileExistsError'>, 'FileNotFoundError': <class 'FileNotFoundError'>, 'IsADirectoryError': <class 'IsADirectoryError'>, 'NotADirectoryError': <class 'NotADirectoryError'>, 'InterruptedError': <class 'InterruptedError'>, 'PermissionError': <class 'PermissionError'>, 'ProcessLookupError': <class 'ProcessLookupError'>, 'TimeoutError': <class 'TimeoutError'>, 'open': <built-in function open>, 'copyright': Copyright (c) 2001-2017 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved., 'credits':     Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
    for supporting Python development.  See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object., '__IPYTHON__': True, 'display': <function display at 0x7f9d893481e0>, 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f9d822f7b70>>}}

Bunlara bile erişimi kapatmamız mümkündür, aşağıdaki bölümde göreceğimiz gibi.

Güvenlik

Dışarıdan alınan bir kodu çalıştırmak her zaman risklidir. exec() ve eval() fonksiyonlarının bilgisayarınıza bir kapı açtığını unutmayın.

Tehlikeyi örneklemek için, yukarıdaki örneği tekrar ele alalım. Siz bir formül beklerken, kötü niyetli bir kullanıcı işletim sisteminizi yönetecek bir komutu bu formülle beraber verebilir. Meselâ, formül sorulduğunda

'x); import os; os.system("touch hello.world");(0,

dizesinin verildiğini varsayalım.

değişken = "x" # Formülde kullanılacak değişken.
formül = 'x); import os; os.system("touch hello.world");(0,'
kod = """
for {0} in range(10):
    print({0}, {1})""".format(değişken, formül)

Bu girdi sonucunda çalıştırılacak kod şöyle olur:

print(kod)

for x in range(10):
    print(x, x); import os; os.system("touch hello.world");(0,)

Burada kötü niyetli kullanıcı beklenen formülü verdikten sonra parantezi kapatmış ve işletim sistemine yönelik komutlar eklemiş. (Sondaki (0, kısmı, kalıpta bulunan sağ parantezin sentaks hatası vermemesi için, onu etkisiz eleman haline dönüştürüyor.)

Bu kodu exec(kod) ile çalıştırdığınızda ekrana sayılar tablosu çıkmasının yanı sıra, bu programı çalıştırdığınız dizinin altında hello.world isimli boş bir dosya yaratıldığını göreceksiniz (Linux kullanıyorsanız). Yani program işletim sisteminize erişebildi. Kötü niyetli bir saldırgan aynı yöntemle diskinizi silebilir, şifrelerinizi çalabilir, virüs yerleştirebilir.

Bu risklere karşı alınabilecek kısmi tedbirler vardır. En yaygın olanı, exec’in çalıştığı sanal ortamdaki değişkenleri, globals ve locals parametreleri kullanarak düzenlemektir. Sözgelişi aşağıda, globals parametresi olarak {"__builtins__":None} vermekle Python’un öntanımlı fonksiyonlarını kapatırız. Böylelikle import ile bir modül yüklenmesini ve işletim sistemine ulaşılmasını engelleriz. Bu işlem range ve print fonksiyonlarını da kapatır, o yüzden locals parametresine bunların tanımlarını içeren bir sözlük veririz.

exec(kod, {"__builtins__":None}, {"range":range, "print":print})
0 0
ImportError: __import__ not found

Bu yeni düzende import fonksiyonu tanınmadığı için exec() çağrısı bir hata verdi ve sızma engellendi. Aynı kodu beklenen şekilde bir girdiyle çalıştırdığınızda ise sorun yaşamazsınız.

Matematik kütüphanesindeki fonksiyonları kullanan işlemler yapmak istiyorsanız, gereken fonksiyonlardan oluşan bir “beyaz liste” oluşturabilirsiniz.

değişken = "x" # Formülde kullanılacak değişken.
formül = "x * sqrt(x+1)/log(x+2)"
kod = """
for {0} in range(10):
    print({0}, {1})""".format(değişken, formül)
import math
exec(kod, {"__builtins__":None}, {"range":range, "print":print, "sqrt":math.sqrt, "log":math.log})
0 0.0
1 1.2872726592996706
2 2.4988211106473432
3 3.728009607357671
4 4.991893199734352
5 6.293943592339911
6 7.6340245917967176
7 9.010908615097694
8 10.423067565678043
9 11.868949934707404

Kendi kullanacağınız programlar veya bir masaüstü uygulaması için fazla endişe etmek gerekmeyebilir. Yanlış veya kötü niyetli bir kullanım sadece kullanıcıya zarar verecektir. Ama bir web uygulaması yazıyorsanız güvenliğe çok daha fazla dikkat etmelisiniz. Bu, daha derin bir uzmanlık gerektirir.


Özetle, dinamik olarak üretilen bir kodu işletmek için exec/eval kullanabilirsiniz. Bunun yararlı olduğu çeşitli durumlar vardır. Söz gelişi, Python sentaksıyla yazılmış bir dizeyi doğrudan alıp işletmek gibi.

exec/eval ilk bakışta çok hoş görünseler de çok sık kullanılmamalıdırlar, bazı sakıncaları vardır.

  • Kodu okumayı zorlaştırır. Programcı kaynak koduna ek olarak, çalıştırılmak üzere alınacak kodun ne olduğunu da bilmelidir.
  • Kodu test etmeyi, hataları bulmayı zorlaştırır.
  • Güvenlik açığı oluşturur.

exec/eval fonksiyonlarının işe yaradığı durumlar vardır, ama probleminizi önce başka yaklaşımlarla çözmeye çalışın.